﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVCBugTracker.Models;
using MVCBugTracker.ViewModels;
using System.IO;
using System.Web.Security;
using System.Net.Mail;
using System.Web.Routing;

namespace MVCBugTracker.Controllers
{
    public class BugController : Controller
    {
        public IMembershipService MembershipService
        {
            get;
            set;
        }
        public IMailDeliveryService MailService
        {
            get;
            set;
        }
        public IProfileRepository ProfileRepository
        {
            get;
            set;
        }
        public IProjectRepository ProjectRepository
        {
            get;
            set;
        }
        public IBugRepository BugRepository
        {
            get;
            set;
        }

        protected override void Initialize(RequestContext requestContext)
        {
            if(MembershipService == null)
            {
                MembershipService = new AccountMembershipService();
            }
            if(MailService == null)
            {
                MailService = new SmtpMailDeliveryService();
            }
            if(ProfileRepository == null)
            {
                ProfileRepository = new EFProfileRepository();
            }
            if(ProjectRepository == null)
            {
                ProjectRepository = new EFProjectRepository();
            }
            if(BugRepository == null)
            {
                BugRepository = new EFBugRepository();
            }

            base.Initialize(requestContext);
        }

        // GET: /Bug/
        [Authorize]
        public ActionResult Index(int? projectID)
        {
            if(projectID == null)
                projectID = ProfileRepository.GetProfile(User.Identity.Name).DefaultProjectID;

            var results = new BugIndexViewModel();
            results.UsersProjects = ProjectRepository.GetMyProjects(User.Identity.Name).ToList();
            if(projectID.HasValue)
            {
                results.ActiveProjectID = projectID;
                results.Bugs = BugRepository.GetBugsByProject(projectID.Value).ToList();
            }
            else
                results.Bugs = new List<Bug>();

            return View(results);
        }

        // GET: /Bug/
        [HttpPost]
        [Authorize]
        public ActionResult Index(int? projectID, FormCollection collection)
        {
            try
            {
                var results = new BugIndexViewModel();
                results.UsersProjects = ProjectRepository.GetMyProjects(User.Identity.Name).ToList();
                results.ActiveProjectID = int.Parse(collection["ActiveProjectID"]);

                return RedirectToAction("Index", "Bug", new
                {
                    projectID = collection["ActiveProjectID"]
                });
            }
            catch
            {
                return RedirectToAction("Index");
            }
        }

        // GET: /Bug/Create
        [Authorize]
        public ActionResult Create(int projectid)
        {
            var results = new BugCreateViewModel();

            var project = ProjectRepository.GetProject(projectid);
            if(project != null)
            {
                results.ProjectName = project.Name;
                results.Bug = new Bug()
                {
                    ProjectID = projectid
                };
            }

            results.ProjectMilestones = ProjectRepository.GetProjectMilestones(projectid).ToList();
            results.ProjectCategories = ProjectRepository.GetProjectCategories(projectid).ToList();
            results.ProjectPriorities = ProjectRepository.GetProjectPriorities(projectid).ToList();
            results.ProjectStatuses = ProjectRepository.GetProjectStatuses(projectid).ToList();
            results.ProjectMembers = ProjectRepository.GetProjectMembers(projectid).ToList();

            if(results.ProjectMilestones.Count > 0)
                results.Bug.MilestoneID = results.ProjectMilestones[0].MilestoneID;
            if(results.ProjectPriorities.Count > 0)
                results.Bug.PriorityID = results.ProjectPriorities[0].PriorityID;
            if(results.ProjectStatuses.Count > 0)
                results.Bug.StatusID = results.ProjectStatuses[0].StatusID;

            return View(results);
        }

        // POST: /Bug/Create
        [HttpPost]
        [Authorize]
        [ValidateInput(false)]
        public ActionResult Create(Bug bug)
        {
            if(ModelState.IsValid)
            {
                try
                {
                    bug.CreatorUserName = User.Identity.Name;
                    bug.DateCreated = DateTime.Now;
                    bug.LastUpdate = DateTime.Now;
                    bug.Deleted = false;

                    BugRepository.AddBug(bug);
                    BugRepository.Save();

                    return RedirectToAction("Index");
                }
                catch(Exception ex)
                {
                    var results = new BugCreateViewModel();
                    results.Message = ex.Message;
                    results.Bug = bug;
                    var project = ProjectRepository.GetProject(bug.ProjectID);
                    if(project != null)
                    {
                        results.ProjectName = project.Name;
                    }

                    results.ProjectMilestones = ProjectRepository.GetProjectMilestones(bug.ProjectID).ToList();
                    results.ProjectCategories = ProjectRepository.GetProjectCategories(bug.ProjectID).ToList();
                    results.ProjectPriorities = ProjectRepository.GetProjectPriorities(bug.ProjectID).ToList();
                    results.ProjectStatuses = ProjectRepository.GetProjectStatuses(bug.ProjectID).ToList();
                    results.ProjectMembers = ProjectRepository.GetProjectMembers(bug.ProjectID).ToList();

                    if(results.ProjectMilestones.Count > 0)
                        results.Bug.MilestoneID = results.ProjectMilestones[0].MilestoneID;
                    if(results.ProjectPriorities.Count > 0)
                        results.Bug.PriorityID = results.ProjectPriorities[0].PriorityID;
                    if(results.ProjectStatuses.Count > 0)
                        results.Bug.StatusID = results.ProjectStatuses[0].StatusID;

                    return View(results);
                }
            }
            else
            {
                var results = new BugCreateViewModel();
                results.Bug = bug;
                var project = ProjectRepository.GetProject(bug.ProjectID);
                if(project != null)
                {
                    results.ProjectName = project.Name;
                }

                results.ProjectMilestones = ProjectRepository.GetProjectMilestones(bug.ProjectID).ToList();
                results.ProjectCategories = ProjectRepository.GetProjectCategories(bug.ProjectID).ToList();
                results.ProjectPriorities = ProjectRepository.GetProjectPriorities(bug.ProjectID).ToList();
                results.ProjectStatuses = ProjectRepository.GetProjectStatuses(bug.ProjectID).ToList();
                results.ProjectMembers = ProjectRepository.GetProjectMembers(bug.ProjectID).ToList();

                if(results.ProjectMilestones.Count > 0)
                    results.Bug.MilestoneID = results.ProjectMilestones[0].MilestoneID;
                if(results.ProjectPriorities.Count > 0)
                    results.Bug.PriorityID = results.ProjectPriorities[0].PriorityID;
                if(results.ProjectStatuses.Count > 0)
                    results.Bug.StatusID = results.ProjectStatuses[0].StatusID;

                return View(results);
            }
        }

        // GET: /Bug/Edit/5
        [Authorize]
        public ActionResult Edit(int id)
        {
            var results = new BugEditViewModel();
            results.Bug = BugRepository.GetBug(id);
            var project = ProjectRepository.GetProject(results.Bug.ProjectID);
            if(project != null)
            {
                results.ProjectName = project.Name;
            }

            results.ProjectMilestones = ProjectRepository.GetProjectMilestones(results.Bug.ProjectID).ToList();
            results.ProjectCategories = ProjectRepository.GetProjectCategories(results.Bug.ProjectID).ToList();
            results.ProjectPriorities = ProjectRepository.GetProjectPriorities(results.Bug.ProjectID).ToList();
            results.ProjectStatuses = ProjectRepository.GetProjectStatuses(results.Bug.ProjectID).ToList();
            results.ProjectMembers = ProjectRepository.GetProjectMembers(results.Bug.ProjectID).ToList();

            results.BugCommentViewModel = new BugCommentViewModel()
            {
                BugID = results.Bug.BugID,
                BugComments = BugRepository.GetBugComments(results.Bug.BugID).ToList()
            };
            results.BugHistoryViewModel = new BugHistoryViewModel()
            {
                BugID = results.Bug.BugID,
                BugHistory = BugRepository.GetBugHistories(results.Bug.BugID).ToList()
            };

            results.BugNotificationViewModel = new BugNotificationViewModel()
            {
                BugID = results.Bug.BugID,
                BugNotifications = BugRepository.GetBugNotifications(results.Bug.BugID).ToList()
            };

            results.BugAttachmentViewModel = new BugAttachmentsViewModel()
            {
                BugID = results.Bug.BugID,
                BugAttachments = BugRepository.GetBugAttachments(results.Bug.BugID).ToList()
            };

            return View(results);
        }

        // POST: /Bug/Edit/5
        [HttpPost]
        [Authorize]
        [ValidateInput(false)]
        public ActionResult Edit(int id, FormCollection collection)
        {
            if(ModelState.IsValid)
            {
                try
                {
                    Bug bug = BugRepository.GetBug(id);
                    //hack: removed reflection history
                    //Bug originalBug = bug.Clone() as Bug;

                    List<object[]> changes = new List<object[]>();
                    object oldValue;

                    if(collection["Bug.MilestoneID"] != null)
                    {
                        oldValue = bug.ProjectMilestone.Name ?? "";
                        bug.MilestoneID = int.Parse(collection["Bug.MilestoneID"]);
                        changes.Add(new object[] { "Milestone", oldValue, bug.ProjectMilestone.Name });
                    }

                    if(collection["Bug.CategoryID"] != null)
                    {
                        oldValue = bug.ProjectCategory.Name ?? "";
                        bug.CategoryID = int.Parse(collection["Bug.CategoryID"]);
                        changes.Add(new object[] { "Category", oldValue, bug.ProjectCategory.Name });
                    }

                    if(collection["Bug.PriorityID"] != null)
                    {
                        oldValue = bug.ProjectPriority.Name ?? "";
                        bug.PriorityID = int.Parse(collection["Bug.PriorityID"]);
                        changes.Add(new object[] { "Priority", oldValue, bug.ProjectPriority.Name });
                    }

                    if(collection["Bug.StatusID"] != null)
                    {
                        oldValue = bug.ProjectStatus.Name ?? "";
                        bug.StatusID = int.Parse(collection["Bug.StatusID"]);
                        changes.Add(new object[] { "Status", oldValue, bug.ProjectStatus.Name });
                    }

                    oldValue = bug.Description ?? "";
                    bug.Description = collection["Bug.Description"];
                    changes.Add(new object[] { "Description", oldValue, bug.Description });

                    oldValue = bug.Title ?? "";
                    bug.Title = collection["Bug.Title"];
                    changes.Add(new object[] { "Title", oldValue, bug.Title });

                    oldValue = bug.AssignedUserName ?? "";
                    bug.AssignedUserName = collection["Bug.AssignedUserName"];
                    changes.Add(new object[] { "Assigned User", oldValue, bug.AssignedUserName });

                    oldValue = bug.LastUpdate;
                    bug.LastUpdate = DateTime.Now;
                    changes.Add(new object[] { "Last Update", oldValue, bug.LastUpdate });

                    BugHistory newHistory = new BugHistory()
                    {
                        BugID = bug.BugID,
                        Changes = bug.GetBugChanges(changes),
                        UserName = User.Identity.Name,
                        DateCreated = DateTime.Now
                    };
                    bug.BugHistories.Add(newHistory);

                    BugRepository.Save();

                    //Send out email notifications
                    foreach(BugNotification notification in BugRepository.GetBugNotifications(id))
                    {
                        MembershipUser user = MembershipService.GetUser(notification.UserName);
                        MailAddress fromAddress = new MailAddress("no-reply@mvcbugtracker.com");
                        string subject = string.Format("Bug {0} has been modified by {1}", id, User.Identity.Name);
                        string body = newHistory.Changes;
                        MailMessage message = new MailMessage()
                        {
                            From = fromAddress,
                            Subject = subject,
                            Body = body,
                            IsBodyHtml = true
                        };
                        MailService.Send(user.Email, message);
                    }

                    return RedirectToAction("Index");
                }
                catch
                {
                    return View();
                }
            }
            else
            {
                return RedirectToAction("Edit", id);
            }

        }

        // GET: /Bug/Delete/5
        [Authorize]
        public ActionResult Delete(int id)
        {
            try
            {
                Bug bug = BugRepository.GetBug(id);

                return View(bug);
            }
            catch
            {
                return View();
            }
        }

        // POST: /Bug/Delete/5
        [HttpPost]
        [Authorize]
        public ActionResult Delete(int id, FormCollection collection)
        {
            try
            {
                Bug bug = BugRepository.GetBug(id);
                BugRepository.DeleteBug(bug);
                BugRepository.Save();

                return RedirectToAction("Index");
            }
            catch
            {
                Bug bug = BugRepository.GetBug(id);

                return View(bug);
            }
        }

        // AJAX: /Bug/DeleteComment/2
        [HttpPost]
        [Authorize]
        public PartialViewResult DeleteComment(int id)
        {
            BugComment comment = BugRepository.GetBugComment(id);
            int bugID = comment.BugID;
            BugRepository.DeleteBugComment(comment);
            BugRepository.Save();


            var results = new BugCommentViewModel
            {
                Message = "Comment has been removed from the bug.",
                BugID = bugID,
                BugComments = BugRepository.GetBugComments(bugID).ToList()
            };

            return PartialView("BugComments", results);
        }

        // AJAX: /Bug/AddComment/2

        [HttpPost]
        [Authorize]
        [ValidateInput(false)]
        public PartialViewResult AddComment(int id, FormCollection collection)
        {
            BugComment comment = new BugComment();
            comment.BugID = id;
            comment.Comment = collection["comment"];
            comment.DateCreated = DateTime.Now;
            comment.UserName = User.Identity.Name;

            BugRepository.AddBugComment(comment);
            BugRepository.Save();

            var results = new BugCommentViewModel
            {
                Message = "Comment has been added to the bug.",
                BugID = comment.BugID,
                BugComments = BugRepository.GetBugComments(comment.BugID).ToList()
            };

            return PartialView("BugComments", results);
        }

        // POST: /Bug/Delete/5
        [HttpPost]
        [Authorize]
        public ActionResult DeleteNotification(int id)
        {
            BugNotification notification = BugRepository.GetBugNotification(id);
            int bugID = notification.BugID;
            BugRepository.DeleteBugNotification(notification);
            BugRepository.Save();

            var results = new BugNotificationViewModel
            {
                Message = "Notification has been deleted from the bug.",
                BugID = bugID,
                BugNotifications = BugRepository.GetBugNotifications(bugID).ToList()
            };

            return PartialView("BugNotifications", results);
        }

        // AJAX: /Bug/AddNotification/2
        [HttpPost]
        [Authorize]
        public PartialViewResult AddNotification(int id)
        {
            BugNotification notification = new BugNotification();
            notification.BugID = id;
            notification.UserName = User.Identity.Name;

            BugRepository.AddBugNotification(notification);
            BugRepository.Save();

            var results = new BugNotificationViewModel
            {
                Message = "Notification has been added to the bug.",
                BugID = notification.BugID,
                BugNotifications = BugRepository.GetBugNotifications(notification.BugID).ToList()
            };

            return PartialView("BugNotifications", results);
        }

        [HttpPost]
        [Authorize]
        public ActionResult AddAttachment(int id, FormCollection collection)
        {
            BugAttachment attachment = new BugAttachment();
            attachment.BugID = id;
            attachment.UserName = User.Identity.Name;
            attachment.ContentType = Request.Files["file"].ContentType;

            Stream fileStream = Request.Files["file"].InputStream;
            attachment.FileName = Path.GetFileName(Request.Files["file"].FileName);
            int fileLength = Request.Files["file"].ContentLength;
            byte[] fileData = new byte[fileLength];
            fileStream.Read(fileData, 0, fileLength);
            attachment.Attachment = fileData;

            BugRepository.AddBugAttachment(attachment);
            BugRepository.Save();

            return RedirectToAction("Edit", "Bug", new
            {
                id = id
            });
        }

        [HttpPost]
        [Authorize]
        public PartialViewResult DeleteAttachment(int id)
        {
            BugAttachment attachment = BugRepository.GetBugAttachment(id);
            int bugID = attachment.BugID;
            BugRepository.DeleteBugAttachment(attachment);
            BugRepository.Save();

            var results = new BugAttachmentsViewModel
            {
                Message = "Attachment has been deleted from the bug.",
                BugID = bugID,
                BugAttachments = BugRepository.GetBugAttachments(bugID).ToList()
            };

            return PartialView("BugAttachments", results);
        }

        [Authorize]
        public FileContentResult GetAttachment(int id)
        {
            BugAttachment attachment = BugRepository.GetBugAttachment(id);

            return File(attachment.Attachment, attachment.ContentType, attachment.FileName);

        }

        [HttpPost]
        [Authorize]
        public ActionResult AddRelated(int id, int relatedID)
        {
            Bug parentBug = BugRepository.GetBug(id);
            Bug relatedBug = BugRepository.GetBug(relatedID);

            if(relatedBug == null)
            {
                ViewData.ModelState.AddModelError("relatedID", "Bug does not exists");
            }
            else
            {
                parentBug.RelatedBugs.Add(relatedBug);

                BugRepository.Save();
            }

            return PartialView("BugRelated", parentBug);
        }

        [Authorize]
        public ActionResult RemoveRelated(int id, int relatedID)
        {
            Bug parentBug = BugRepository.GetBug(id);
            Bug relatedBug = BugRepository.GetBug(relatedID);

            parentBug.RelatedBugs.Remove(relatedBug);

            BugRepository.Save();

            return PartialView("BugRelated", parentBug);
        }      
    }
}
